package org.kazuhiko.rpg.server.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

import org.kazuhiko.rpg.server.Server;
import org.kazuhiko.rpg.server.model.Player;
import org.kazuhiko.rpg.server.util.OS_Type;
import org.kazuhiko.rpg.server.util.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ServerFrame extends JFrame
{
	private static final long serialVersionUID = -4161669002156704398L;
	
	private static final Logger logger = LoggerFactory.getLogger( ServerFrame.class );
	
	private JTabbedPane tabbedPane;
	
	private JTextPane jtpCommands;
	
	private JPanel pnlUser, pnlUserInfo;
	
	private JLabel lblUserPosition, lblUserRotation, lblUserId;
	
	private ImageIcon iiUser, iiCommands;
	
	private JTextField txtCommand;
	
	private DefaultListModel<String> worldListModel, playerListModel;
	
	private JList<String> worldList, playerList;
	
	private Timer timer;
	
	public ServerFrame()
	{
		init();
		initTimer();
	}
	
	private void init()
	{
		optimizeLAF();
		setTitle( "RPG Server 1.0" );
		setResizable( true );
		setPreferredSize( new Dimension( 800, 600 ) );
		setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
		
		tabbedPane = new JTabbedPane();
		
		add( tabbedPane, BorderLayout.CENTER );
		
		createUserPanel();
		createCommandPanel();
		fillWorldList();
		
		
		
		addWindowListener( new WindowListener()
		{	
			@Override
			public void windowOpened( WindowEvent we )
			{
			}
			
			@Override
			public void windowIconified( WindowEvent we )
			{		
			}
			
			@Override
			public void windowDeiconified( WindowEvent we )
			{	
			}
			
			@Override
			public void windowDeactivated( WindowEvent we )
			{		
			}
			
			@Override
			public void windowClosing( WindowEvent we )
			{
				close( false );
			}
			
			@Override
			public void windowClosed( WindowEvent we )
			{		
			}
			
			@Override
			public void windowActivated( WindowEvent we )
			{	
			}
		});
	}
	
	private void initTimer()
	{
		ActionListener taskPerformer = new ActionListener()
		{	
			@Override
			public void actionPerformed( ActionEvent ae )
			{
				checkLists();
			}
		};
		
		timer = new Timer( 500, taskPerformer );
		timer.start();
	}
	
	private void createUserPanel()
	{
		pnlUser = new JPanel();
		pnlUser.setLayout( new BorderLayout() );
		
		iiUser = Util.createImageIcon( "/resources/images/usersIcon.png" );
		
		tabbedPane.addTab( "Users", iiUser, pnlUser );
		
		JPanel worldListPanel = new JPanel();
		worldListPanel.setLayout( new BorderLayout() );
		
		worldListModel = new DefaultListModel<String>();
		worldList = new JList<String>( worldListModel );
		worldList.setBorder( BorderFactory.createLineBorder( Color.black ) );
		worldList.addListSelectionListener( new ListSelectionListener()
		{	
			@Override
			public void valueChanged(ListSelectionEvent lse )
			{
				fillPlayerList();
			}
		});
		
		JScrollPane scrollPane = new JScrollPane( worldList );
		
		worldListPanel.add( new JPanel(), BorderLayout.WEST ); //Empty panel for west ( padding )
		worldListPanel.add( scrollPane, BorderLayout.CENTER ); //The list in the middle
		worldListPanel.add( new JPanel(), BorderLayout.EAST ); //Empty panel for the east ( padding )
		
		pnlUser.add( worldListPanel, BorderLayout.WEST );
		
		JPanel playerListPanel = new JPanel();
		playerListPanel.setLayout( new BorderLayout() );
		
		playerListModel = new DefaultListModel<String>();
		playerList = new JList<String>( playerListModel );
		playerList.setBorder( BorderFactory.createLineBorder( Color.black ) );
		
		scrollPane = new JScrollPane( playerList );
		
		playerListPanel.add( new JPanel(), BorderLayout.WEST ); //Empty panel for west ( padding )
		playerListPanel.add( scrollPane, BorderLayout.CENTER ); //The list in the middle
		playerListPanel.add( new JPanel(), BorderLayout.EAST ); //Empty panel for the east ( padding )
		
		pnlUser.add( playerListPanel, BorderLayout.CENTER );
		
		pnlUserInfo = new JPanel();
		pnlUserInfo.setLayout( null );
		pnlUserInfo.setPreferredSize( new Dimension( 300, 100 ) );
		pnlUserInfo.setVisible( false );
		
		JLabel lblUserInfo = new JLabel();
		lblUserInfo.setText( "User information: " );
		lblUserInfo.setBounds( 10, 20, 100, 20 );
		pnlUserInfo.add( lblUserInfo );
		
		lblUserPosition = new JLabel();
		lblUserPosition.setText( "Position: [x=0, y=0, z=0]" );
		lblUserPosition.setBounds( 10, 50, 250, 20 );
		pnlUserInfo.add( lblUserPosition );
		
		lblUserRotation = new JLabel();
		lblUserRotation.setText( "Rotation: [pitch=0, yaw=0, roll=0]" );
		lblUserRotation.setBounds( 10, 70, 250, 20 );
		pnlUserInfo.add( lblUserRotation );
		
		lblUserId = new JLabel();
		lblUserId.setText( "ID #-1" );
		lblUserId.setBounds( 10, 30, 250, 20 );
		pnlUserInfo.add( lblUserId );
		
		pnlUser.add( pnlUserInfo, BorderLayout.EAST );
	}
	
	private void createCommandPanel()
	{
		JPanel pnlCommands = new JPanel();
		pnlCommands.setLayout( new BorderLayout() );
		
		jtpCommands = new JTextPane();
		jtpCommands.setEditable( false );
		jtpCommands.setBackground( Color.black );
		
		JScrollPane jspCommands = new JScrollPane( jtpCommands );
		
		pnlCommands.add( jspCommands, BorderLayout.CENTER );
		
		txtCommand = new JTextField();
		txtCommand.setBackground( Color.black );
		txtCommand.setForeground( Color.green );
		
		StyledDocument doc = jtpCommands.getStyledDocument();
		Style def = StyleContext.getDefaultStyleContext().getStyle( StyleContext.DEFAULT_STYLE );
		
		Style command = doc.addStyle( CommandType.COMMAND.toString(), def );
		StyleConstants.setForeground( command, CommandType.COMMAND.getTextColor() );
		
		Style notFound = doc.addStyle( CommandType.NOT_FOUND.toString(), def );
		StyleConstants.setForeground( notFound, CommandType.NOT_FOUND.getTextColor() );
		
		Style teleport = doc.addStyle( CommandType.TELEPORT.toString(), def );
		StyleConstants.setForeground( teleport, CommandType.TELEPORT.getTextColor() );
		
		txtCommand.addKeyListener( new KeyListener()
		{		
			@Override
			public void keyTyped(KeyEvent ke )
			{
			}
			
			@Override
			public void keyReleased(KeyEvent ke )
			{
				if( ke.getKeyCode() == KeyEvent.VK_ENTER && !txtCommand.getText().equals( "" ) )
				{
					checkCommand( txtCommand.getText() );
					
					txtCommand.setText( "" );
				}
			}
			
			@Override
			public void keyPressed(KeyEvent ke )
			{		
			}
		});
		
		pnlCommands.add( txtCommand, BorderLayout.SOUTH );
		
		iiCommands = Util.createImageIcon( "/resources/images/consoleIcon.png" );
		
		tabbedPane.addTab( "Commands", iiCommands, pnlCommands );
	}
	
	private void checkCommand( String command )
	{
		String[] commandSplit = command.split( " " );
		CommandType cmt = CommandType.NOT_FOUND;
		
		if( commandSplit.length > 1 )
		{
			try
			{
				cmt = CommandType.valueOf( commandSplit[0].toUpperCase() );
				
				executeCommand( cmt, commandSplit );
			}
			catch( IllegalArgumentException e )
			{
				addTextToCommandPlane( "Command '" + commandSplit[0] + "' not found.", CommandType.NOT_FOUND );
			}
		}
		else
		{
			addTextToCommandPlane( "No command given.", CommandType.NOT_FOUND );
		}
	}
	
	private void executeCommand( CommandType cmt, String[] values )
	{
		if( cmt == CommandType.TELEPORT && values.length == 5 )
		{
			addTextToCommandPlane( "Teleporting " + values[1] + " to x:" + values[2] + ", y:" + values[3] + ", z:" + values[4], CommandType.COMMAND );
		}
		else
		{
			addTextToCommandPlane( "Invalid parameters for function 'teleport'", CommandType.NOT_FOUND );
		}
	}
	
	private void addTextToCommandPlane( String text, CommandType style )
	{
		StyledDocument doc = jtpCommands.getStyledDocument();
				
		try
		{
			doc.insertString( doc.getLength(), Util.getDate( "HH:mm:ss" ) + "\t" + text + "\r\n" , doc.getStyle( style.toString() ) );
		}
		catch( Exception e)
		{
			logger.error( "Can't add text: " + text );
		}
	}
	
	private void fillWorldList()
	{
		for( String worldName : Server.getServer().getWorldNames() )
		{
			if( !listHasValue( worldListModel, worldName ) )
			{
				worldListModel.addElement( worldName );
			}
		}
	}
	
	private void fillPlayerList()
	{
		if( worldList.getSelectedValue() != null )
		{
			for( Player player : Server.getServer().getWorld( worldList.getSelectedValue().toString() ).getPlayerList().getPlayers() )
			{
				if( !listHasValue( playerListModel, player.getName() ) )
				{
					playerListModel.addElement( player.getName() );
				}
			}
		}
	}
	
	private boolean listHasValue( DefaultListModel<String> model, String value )
	{
		boolean found = false;
		
		for( int i = 0; i < model.getSize() && !found; i++ )
		{
			if( model.get( i ).equals( value ) )
			{
				found = true;
			}
		}
		
		return found;
	}
	
	private void checkLists()
	{
		//Checking the worlds
		for( int i = 0; i < worldListModel.getSize(); i++ )
		{
			if( !Server.getServer().getWorldNames().contains( worldListModel.get( i ) ) )
			{
				worldListModel.remove( i );
			}
		}
		
		fillWorldList();
		
		if( worldList.getSelectedValue() != null )
		{	
			for( int i = 0; i < playerListModel.size(); i++ )
			{
				if( !Server.getServer().getWorld( worldList.getSelectedValue().toString() ).getPlayerList().hasUsername( playerListModel.get( i ).toString() ) )
				{
					playerListModel.remove( i );
				}
			}
		}
		
		fillPlayerList();
			
		if( playerList.getSelectedValue() != null )
		{
			Player player = Server.getServer().getWorld( worldList.getSelectedValue().toString() ).getPlayerList().getPlayer( playerList.getSelectedValue().toString() ); //Mother of chains
			pnlUserInfo.setVisible( true );
			lblUserPosition.setText( "Position: [x=" + player.getPosition().getX() + ", y=" + player.getPosition().getY() + ", z=" + player.getPosition().getZ() + "]" );
			lblUserRotation.setText( "Rotation: [pitch=" + player.getRotation().getPitch() + ", yaw=" + player.getRotation().getYaw() + ", roll=" + player.getRotation().getRoll() + "]" );
			lblUserId.setText( "ID #" + player.getId() );
		}
		else
		{
			pnlUserInfo.setVisible( false );
			lblUserPosition.setText( "Position: [x=0, y=0, z=0]" );
			lblUserRotation.setText( "Rotation: [pitch=0, yaw=0, roll=0]" );
		}
	}
	
	private void optimizeLAF()
	{
		try
		{
			for( LookAndFeelInfo info : UIManager.getInstalledLookAndFeels() )
			{
				if( Util.checkOS( OS_Type.WINDOWS ) )
				{
					if( "Windows".equals( info.getName() ) )
					{
						UIManager.setLookAndFeel( info.getClassName() );
						break;
					}
				}
				
			}
		}
		catch( Exception ex )
		{
			ex.printStackTrace();
		}
	}
	
	public void showFrame()
	{
		pack();
		setVisible( true );
	}
	
	private void close( boolean error )
	{
		Server.getServer().stop();
		System.exit( ( error )? 1 : 0 );
	}
}
